package cn.teaseed.view;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import cn.teaseed.Main;
import cn.teaseed.OSC.oscin.VRCListener;
import cn.teaseed.comn.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.input.MouseButton;
import javafx.stage.Stage;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class HomeView {
    public MenuItem jobs;
    public TableView<JobEntity> jobList;
    public TableColumn<JobEntity, String> listenerColumn;
    public TableColumn<JobEntity, String> jobMethodColumn;
    public TableColumn<JobEntity, String> callbackColumn;
    public TableColumn<JobEntity, String> remark;
    public TextArea logList;

    @Resource
    private VRCListener vrcListener;
    @Resource
    private WebSocketServer webSocketServer;
    @Resource
    private CronJobServer cronJobServer;

    public static SettingEntity localData;

    public static Map<String, JobEntity> urlMap = new HashMap<>();

    @FXML
    private void initialize() throws IOException {
        //设置列的值工厂
        listenerColumn.setCellValueFactory(cellData -> cellData.getValue().listenerProperty());
        jobMethodColumn.setCellValueFactory(cellData -> cellData.getValue().jobMethodProperty());
        callbackColumn.setCellValueFactory(cellData -> cellData.getValue().callbackProperty());
        remark.setCellValueFactory(cellData -> cellData.getValue().remarkProperty());

        //创建右键菜单
        ContextMenu contextMenu = new ContextMenu();
        MenuItem deleteItem = new MenuItem("配置");
        deleteItem.setOnAction(event -> {
            JobEntity selectedItem = jobList.getSelectionModel().getSelectedItem();
            CorsParam.fifoCache.put("Config", selectedItem);
            switch (selectedItem.getJobMethod()) {
                case "DG-LAB": {
                    try {
                        Stage stage = Main.newWindow(jobList.getScene(), "config-DGLAB.fxml", false);
                        double x = stage.getX() + 50; // 水平偏移量
                        double y = stage.getY() + 50; // 垂直偏移量
                        stage.setX(x);
                        stage.setY(y);
                        stage.setTitle("配置任务");
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    break;
                }
                case "Http": {
                    try {
                        Stage stage = Main.newWindow(jobList.getScene(), "config-Http.fxml", false);
                        double x = stage.getX() + 50; // 水平偏移量
                        double y = stage.getY() + 50; // 垂直偏移量
                        stage.setX(x);
                        stage.setY(y);
                        stage.setTitle("配置任务");
                    } catch (Exception e) {
                        throw new RuntimeException(e);
                    }
                    break;
                }
            }
        });
        MenuItem deleteItem2 = new MenuItem("删除");
        deleteItem2.setOnAction(event -> {
            // 处理删除操作
            JobEntity selectedItem = jobList.getSelectionModel().getSelectedItem();
            cronJobServer.removeTask(selectedItem.getId());
            jobList.getItems().removeAll(selectedItem);
            localData.setHomeJobList(jobList.getItems());
            refreshJob();
            if (localData.getHomeJobList() != null && !localData.getHomeJobList().isEmpty()) {
                try {
                    CorsParam.upDateDataBaseJson(localData);
                } catch (Exception e) {
                    log.error("保存时出现异常：{}", e.getMessage());
                }
            }
        });
        ObservableList<MenuItem> items = contextMenu.getItems();
        items.add(deleteItem);
        items.add(deleteItem2);

        // 为TableView设置右键菜单
        jobList.setOnMouseClicked(event -> {
            //显示右键菜单
            if (event.getButton() == MouseButton.SECONDARY && event.getClickCount() == 1) {
                if (!jobList.getSelectionModel().isEmpty()) {
                    contextMenu.show(jobList, event.getScreenX(), event.getScreenY());
                }
            }
            //左键关闭菜单
            if (event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 1) {
                if (!jobList.getSelectionModel().isEmpty()) {
                    contextMenu.hide();
                }
            }
        });

        String dataJson = CorsParam.getDataJson();
        localData = JSONUtil.toBean(dataJson, JSONConfig.create(), SettingEntity.class);
        //我不理解为什么toBean不能把JSON的String类型转换成StringProperty暂时手动转换 等搞懂这个神经病哪里出问题再说
        List<JobEntityJson> list = JSONUtil
                .toList(JSONUtil.getByPath(JSONUtil.parse(dataJson),
                                "$.homeJobList").toString(),
                        JobEntityJson.class);
        if (list != null && !list.isEmpty()) {
            List<JobEntity> add = new ArrayList<>();
            list.forEach(e -> {
                JobEntity es = BeanUtil.copyProperties(e, JobEntity.class);
                es.setListener(e.getListener());
                es.setJobMethod(e.getJobMethod());
                es.setCallback(e.getCallback());
                es.setRemark(e.getRemark());
                add.add(es);
            });
            localData.setHomeJobList(add);
            urlMap = add.stream().collect(Collectors.toMap(JobEntity::getListener, jobList -> jobList));
            onlyTime = add.stream()
                    .filter(entity -> {
                        //防止空
                        if (StrUtil.isEmpty(entity.getWaveATime()) && StrUtil.isEmpty(entity.getWaveBTime()))
                            return false;
                        try {
                            //防止字母
                            int i = Integer.parseInt(entity.getWaveATime());
                            int i1 = Integer.parseInt(entity.getWaveBTime());
                        } catch (Exception e) {
                            return false;
                        }
                        return true;
                    })
                    .collect(Collectors.toMap(
                            JobEntity::getListener,
                            e -> Math.max(Integer.parseInt(e.getWaveATime()), Integer.parseInt(e.getWaveBTime()))
                    ));
        } else {
            localData.setHomeJobList(null);
        }
        //初始化数据源
        if (localData.getHomeJobList() != null && !localData.getHomeJobList().isEmpty()) {
            ObservableList<JobEntity> data = FXCollections.observableArrayList();
            data.addAll(localData.getHomeJobList());
            jobList.setItems(data);
            localData.getHomeJobList().forEach(e -> {
                switch (e.getJobMethod()) {
                    case "DG-LAB": {
                        //添加监听
                        vrcListener.addListener(e.getListener(), (d, m) -> {
                            String string = m.getArguments().get(0).toString();
                            trySendDGLab(e.getListener(), string);
                        });
                        log.info("已启动[{}]监听", e.getListener());
                        break;
                    }
                    case "Http": {
                        cronJobServer.submitHttpJob(e.getId());
                        log.info("已启动[{}]Http轮询", e.getListener());
                        break;
                    }
                }
            });
            log.info("已加载{}个配置数据", localData.getHomeJobList().size());
        }

        // 定时任务设置,支持秒级别定时任务
        CronUtil.setMatchSecond(true);
        //开启定时任务
        CronUtil.start(true);

        //设置日志
//        ConsoleOutputRedirector.wrap(logList, 100L);
        logList.setEditable(false);

    }

    public void openSetting(ActionEvent actionEvent) throws Exception {
        Stage stage = Main.newWindow(jobList.getScene(), "config.fxml", false);
        double x = stage.getX() + 50; // 水平偏移量
        double y = stage.getY() + 50; // 垂直偏移量
        stage.setX(x);
        stage.setY(y);
        stage.setTitle("配置任务");
    }

    public void addJob(JobEntity entity) {
        jobList.getItems().add(entity);
        localData.setHomeJobList(jobList.getItems());
        urlMap = jobList.getItems().stream().collect(Collectors.toMap(JobEntity::getListener, jobList -> jobList));
        onlyTime = jobList.getItems().stream()
                .filter(e -> {
                    //防止空
                    if (StrUtil.isEmpty(e.getWaveATime()) && StrUtil.isEmpty(e.getWaveBTime())) return false;
                    try {
                        //防止字母
                        int i = Integer.parseInt(e.getWaveATime());
                        int i1 = Integer.parseInt(e.getWaveBTime());
                    } catch (Exception ex) {
                        return false;
                    }
                    return true;
                })
                .collect(Collectors.toMap(
                        JobEntity::getListener,
                        e -> Math.max(Integer.parseInt(e.getWaveATime()), Integer.parseInt(e.getWaveBTime()))
                ));
        switch (entity.getJobMethod()) {
            case "DG-LAB": {
                //添加监听
                vrcListener.addListener(entity.getListener(), (data, message) -> {
                    String string = message.getArguments().get(0).toString();
                    trySendDGLab(entity.getListener(), string);
                });
                break;
            }
            case "Http": {
                cronJobServer.submitHttpJob(entity.getId());
                break;
            }
        }
        try {
            CorsParam.upDateDataBaseJson(localData);
        } catch (Exception e) {
            log.error("保存时出现异常：{}", e.getMessage());
        }
    }

    public void refreshJob() {
        jobList.refresh();
        localData.setHomeJobList(jobList.getItems());
        urlMap = jobList.getItems().stream().collect(Collectors.toMap(JobEntity::getListener, jobList -> jobList));
        onlyTime = jobList.getItems().stream()
                .filter(e -> {
                    //防止空
                    if (StrUtil.isEmpty(e.getWaveATime()) && StrUtil.isEmpty(e.getWaveBTime())) return false;
                    try {
                        //防止字母
                        int i = Integer.parseInt(e.getWaveATime());
                        int i1 = Integer.parseInt(e.getWaveBTime());
                    } catch (Exception ex) {
                        return false;
                    }
                    return true;
                })
                .collect(Collectors.toMap(
                        JobEntity::getListener,
                        e -> Math.max(Integer.parseInt(e.getWaveATime()), Integer.parseInt(e.getWaveBTime()))
                ));
        localData.setWaveMap(CorsParam.WaveMap);
        localData.setBleDevice(CorsParam.BleDevice);
        try {
            CorsParam.upDateDataBaseJson(localData);
        } catch (Exception e) {
            log.error("保存时出现异常：{}", e.getMessage());
        }
    }

    public void openSocketLink(ActionEvent actionEvent) throws Exception {
        Stage stage = Main.newWindow(jobList.getScene(), "dGLabImage.fxml", false);
        double x = stage.getX() + 50; // 水平偏移量
        double y = stage.getY() + 50; // 垂直偏移量
        stage.setX(x);
        stage.setY(y);
        stage.setTitle("Socket连接");
    }

    private final Map<String, Long> lastTimeMap = new HashMap<>();
    private Map<String, Integer> onlyTime = new HashMap<>();

    private void trySendDGLab(String url, String message) {
        long lastInvocationTimeMillis = lastTimeMap.get(url) == null ? 0L : lastTimeMap.get(url);
        long currentTimeMillis = System.currentTimeMillis();
        long timeSinceLastInvocation = currentTimeMillis - lastInvocationTimeMillis;
        //取不到波形时长或小于波形持续时间 则不执行逻辑 若设置了两个通道数据 则按最长时间计算
        if (onlyTime.get(url) == null || timeSinceLastInvocation < (onlyTime.get(url) * 1000)) return;
        // 执行方法逻辑
        JobEntity job = urlMap.get(url);
        if (job != null) {
            if (job.getWaveA() != null && !job.getWaveA().isEmpty()) {
                //A通道发送强度 无强度设置则为App端当前默认
                if (job.getWaveAStrength() != null && !job.getWaveAStrength().isEmpty()) {
                    //加载配置强度
                    String strength = "strength-1+2+{}";
                    int waveAStrength = Integer.parseInt(job.getWaveAStrength());
                    int waveAStrengthMax = Integer.parseInt(CorsParam.WaveAStrengthMax);
                    int min = Math.min(waveAStrength, waveAStrengthMax);

                    //启用相对强度
                    if (job.getRelativeStrength() && isNumber(message)) {
                        //近似相对值计算
                        String maxListenerValue = job.getMaxListenerValue();
                        BigDecimal decimal = new BigDecimal(message)
                                .divide(new BigDecimal(maxListenerValue), 2, RoundingMode.HALF_UP);
                        BigDecimal minDec = decimal.multiply(new BigDecimal(CorsParam.WaveAStrengthMax));
                        min = minDec.intValue();
                    }

                    //配置强度高于App强度 以App强度为准
                    DataMessage da = new DataMessage();
                    da.setMessage(StrUtil.format(strength, min));
                    da.setType("msg");
                    webSocketServer.sendNoArg(da);
                }
                //A通道 发送波形
                List<String> wave = CorsParam.WaveMap.get(job.getWaveA());
                if (StrUtil.isNotEmpty(job.getWaveATime()) && isNumber(job.getWaveATime()) && !job.getWaveATime().equals("1")) {
                    //推算时间
                    List<String> wa = new ArrayList<>();
                    int time = Math.min(Integer.parseInt(job.getWaveATime()), 7);
                    for (int i = 0; i < time; i++) {
                        wa.addAll(wave);
                    }
                    wave = wa;
                }
                DataMessage da = new DataMessage();
                da.setMessage("pulse-A:" + JSONUtil.toJsonStr(wave));
                da.setType("msg");
                webSocketServer.sendNoArg(da);
            }
            if (job.getWaveB() != null && !job.getWaveB().isEmpty()) {
                //B通道发送强度 无强度设置则为App端当前默认
                if (job.getWaveBStrength() != null && !job.getWaveBStrength().isEmpty()) {
                    String strength = "strength-2+2+{}";
                    int waveBStrength = Integer.parseInt(job.getWaveBStrength());
                    int waveBStrengthMax = Integer.parseInt(CorsParam.WaveAStrengthMax);
                    int min = Math.min(waveBStrength, waveBStrengthMax);

                    //启用相对强度
                    if (job.getRelativeStrength() && isNumber(message)) {
                        //近似相对值计算
                        String maxListenerValue = job.getMaxListenerValue();
                        BigDecimal decimal = new BigDecimal(message)
                                .divide(new BigDecimal(maxListenerValue), 2, RoundingMode.HALF_UP);
                        BigDecimal minDec = decimal.multiply(new BigDecimal(CorsParam.WaveAStrengthMax));
                        min = minDec.intValue();
                    }

                    //配置强度高于App强度 以App强度为准
                    DataMessage da = new DataMessage();
                    da.setMessage(StrUtil.format(strength, min));
                    da.setType("msg");
                    webSocketServer.sendNoArg(da);
                }
                //B通道 发送波形
                List<String> wave = CorsParam.WaveMap.get(job.getWaveB());
                if (StrUtil.isNotEmpty(job.getWaveBTime()) && isNumber(job.getWaveBTime()) && !job.getWaveBTime().equals("1")) {
                    //推算时间
                    List<String> wa = new ArrayList<>();
                    int time = Math.min(Integer.parseInt(job.getWaveBTime()), 7);
                    for (int i = 0; i < time; i++) {
                        wa.addAll(wave);
                    }
                    wave = wa;
                }
                DataMessage da = new DataMessage();
                da.setMessage("pulse-B:" + JSONUtil.toJsonStr(wave));
                da.setType("msg");
                webSocketServer.sendNoArg(da);
            }
        }
        // 更新最后调用时间
        lastTimeMap.put(url, System.currentTimeMillis());
    }

    private static boolean isNumber(String str) {
        try {
            Double.parseDouble(str);
            return true;
        } catch (NumberFormatException e) {
            return false;
        }
    }

    public void openWave(ActionEvent actionEvent) throws Exception {
        Stage stage = Main.newWindow(jobList.getScene(), "wave.fxml", false);
        double x = stage.getX() + 50; // 水平偏移量
        double y = stage.getY() + 50; // 垂直偏移量
        stage.setX(x);
        stage.setY(y);
        stage.setTitle("DG-LAB波形配置");
        stage.setResizable(false);
    }

    public void openModelSearch(ActionEvent actionEvent) throws Exception {
        Stage stage = Main.newWindow(jobList.getScene(), "modelSearch.fxml", false);
        double x = stage.getX() + 50; // 水平偏移量
        double y = stage.getY() + 50; // 垂直偏移量
        stage.setX(x);
        stage.setY(y);
        stage.setTitle("模型参数搜索");
        stage.setResizable(false);
    }

    public void openBleScan(ActionEvent actionEvent) throws Exception {
        Stage stage = Main.newWindow(jobList.getScene(), "bleDeviceScan.fxml", false);
        double x = stage.getX() + 50; // 水平偏移量
        double y = stage.getY() + 50; // 垂直偏移量
        stage.setX(x);
        stage.setY(y);
        stage.setTitle("心率广播设备搜索");
        stage.setResizable(false);
    }

    public void test(ActionEvent actionEvent) {
        vrcListener.addListener("/avatar/parameters/AngularY", (d, m) -> {
            String string = m.getArguments().get(0).toString();
            log.info("OSCData:{}",string);
        });

        log.info("已启动[test]监听");
    }
}
